home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc Source Code / Imaging / Rgn2PlyM.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-22  |  7.4 KB  |  306 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        Rgn2PlyM.cpp
  3.  
  4.     Contains:    Converting a Mac Region into an ODPolygon.
  5.  
  6.     Owned by:    Jens Alfke    [from an idea by Steve Smith]
  7.  
  8.     Copyright:    © 1994 - 1995 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <2>     2/24/95    jpa        Updated for recent API changes.
  13.          <1>     6/15/94    jpa        first checked in
  14.          ---------------------------Moved to ODSOM project.
  15.          <1>      5/9/94    jpa        first checked in
  16.  
  17.     To Do:
  18.     
  19.         $$$$$ More exception handling
  20.     
  21.     Theory Of Operation:
  22.     
  23.         Apple's patent prevents us from groping the internals of the Region. So we
  24.         draw the region into an offscreen bitmap (banding if necessary, if the whole
  25.         bitmap takes up too much memory) and do a standard image-processing-type scan
  26.         through the bitmap looking for corners. We combine four adjacent pixels into
  27.         a four bit integer and use a switch statement to handle the 16 cases (only 10
  28.         of which represent corners.) Once we know that we can keep track of edges and
  29.         contours, closing or merging contours when they join.
  30.     
  31.     In Progress:
  32.         
  33. */
  34.  
  35.  
  36. #ifndef _ALTPOINT_
  37. #include <AltPoint.h>            // Use C++ savvy ODPoint and ODRect
  38. #endif
  39.  
  40. #ifndef _ALTPOLY_
  41. #include <AltPoly.h>
  42. #endif
  43.  
  44.  
  45. #ifndef _ODTYPES_
  46. #include <ODTypes.h>
  47. #endif
  48.  
  49. #ifndef _RGN2PLYM_
  50. #include "Rgn2PlyM.h"
  51. #endif
  52.  
  53. #ifndef _PGPOLY_
  54. #include "PGPoly.h"
  55. #endif
  56.  
  57. #ifndef _LIST_
  58. #include <List.h>
  59. #endif
  60.  
  61. #ifndef _ODMEM_
  62. #include <ODMemory.h>
  63. #endif
  64.  
  65. #ifndef _EXCEPT_
  66. #include <Except.h>
  67. #endif
  68.  
  69. #ifndef __TOOLUTILS__
  70. #include <ToolUtils.h>        // $$$$$ Just for cursor stuff
  71. #endif
  72.  
  73.  
  74. const long kMaxBitmapSize = 16384;        // Max amt of memory to allocate for bitmap
  75.  
  76.  
  77. #pragma segment ODShape
  78.  
  79.  
  80. static short
  81. AllocBitmap( BitMap &bits, const Rect &bounds )
  82. {
  83.     bits.rowBytes = (((bounds.right-bounds.left)+31) >>5) <<2;    // width/8, rounded up to mult of 4
  84.     short height = bounds.bottom-bounds.top;
  85.     
  86.     bits.baseAddr = kODNULL;
  87.     do{
  88.         long size = (long)height * (long)bits.rowBytes;
  89.         if( size <= kMaxBitmapSize ) {
  90.             TRY{
  91.                 bits.baseAddr = (Ptr)ODNewPtr(size);    // Allocate bitmap
  92.             }CATCH(kODErrOutOfMemory){
  93.                 // Ignore out-of-memory but leave baseAddr NULL
  94.             }ENDTRY
  95.         }
  96.         
  97.         if( bits.baseAddr == kODNULL )
  98.             if( height>3 )
  99.                 height = (height+3)>>1;                    // Too big? Halve the number of rows
  100.             else
  101.                 THROW(kODErrOutOfMemory);                // Can't allocate even 3 rows? Give up
  102.     }while( bits.baseAddr == kODNULL );
  103.     
  104.     // Put the bitmap bounds just above the original bounds. This will force them to be
  105.     // moved (and the bitmap filled) on the first scanline.
  106.     SetRect(&bits.bounds, bounds.left,  bounds.top-height,
  107.                           bounds.right, bounds.top);
  108.     return height;
  109. }
  110.  
  111.  
  112. static short
  113. SetUpPort( GrafPtr port, const Rect &bounds )
  114. {
  115.     OpenPort(port);
  116.     short height = AllocBitmap(port->portBits, bounds);
  117.     port->portRect = port->portBits.bounds;
  118.     RectRgn(port->visRgn, &port->portBits.bounds);
  119.     return height;
  120. }
  121.  
  122.  
  123. static void
  124. DisposePort( GrafPtr port )
  125. {
  126.     ODDisposePtr(port->portBits.baseAddr);
  127.     ClosePort(port);
  128. }
  129.  
  130.  
  131. static void
  132. CloseContour( PGOutputContour *first, PGOutputContour *last, short x, short y,
  133.               PGOutputContour* vert[], const Rect &b, LinkedList &output )
  134. {
  135.     first->AddVertex(kRight,x,y);
  136.     if( first == last ) {
  137.         // Closing a polygon: add to output list.
  138.         output.AddLast(first);
  139.         
  140.     } else {
  141.         // Merging last with first; find other end of last and change to first.
  142.         first->AppendContour(last,kRight);
  143.         
  144.         for( short i=b.right-b.left-1; i>=0; i-- )
  145.             if( vert[i]==last && b.left+i!=x ) {
  146.                 vert[i] = first;
  147.                 break;
  148.             }
  149.         WASSERTM(i>=0,"Missing other end of contour!");
  150.     }
  151. }
  152.  
  153.  
  154. void
  155. Rgn2Poly( RgnHandle rgn, ODPolygon &poly )
  156. {
  157.     Rect b = (**rgn).rgnBBox;
  158.     if( GetHandleSize((Handle)rgn) == sizeof(Region) ) {
  159.         poly.SetRect(b);
  160.         return;                                // Region is rectangular!
  161.     }
  162.         
  163. //    SetCursor(*GetCursor(plusCursor));    // $$$$$ TEST
  164.  
  165.     b.right++;                                                // Need blank space on right/top/bottom
  166.     b.top--;
  167.     b.bottom++;
  168.     
  169.     // Vert is an array that remembers which polygon corresponds to the vertical edge
  170.     // (if any) currently at x. Correspondingly, curHoriz holds the current horizontal
  171.     // edge at this scanline.
  172.     PGOutputContour *curHoriz = kODNULL;
  173.     PGOutputContour* *vert = new PGOutputContour* [b.right-b.left];
  174.     memset(vert,kODNULL, (b.right-b.left)*sizeof(PGOutputContour*));
  175.     
  176.     PGContourList output;
  177.         
  178.     GrafPtr curPort;
  179.     GetPort(&curPort);
  180.     GrafPort port;
  181.     short portHeight = SetUpPort(&port, b);
  182.     
  183.     long *row0, *row1 = kODNULL;        // NULL unnecessary but prevents compiler warnings
  184.     
  185.     for( short y = b.top+1; y<b.bottom; y++ ) {
  186.         if( y>=port.portBits.bounds.bottom ) {                // Scroll next band into view
  187.             SetPort(&port);
  188.             SetOrigin(b.left,y-1);
  189.             ClipRect(&port.portRect);
  190.             EraseRect(&port.portRect);
  191.             PaintRgn(rgn);
  192.             SetPort(curPort);
  193.             
  194.             row0 = (long*)(port.portBits.baseAddr);
  195.         } else
  196.             row0 = row1;
  197.         row1 = (long*)( (char*)row0 + port.portBits.rowBytes);
  198.         
  199.         long *srcp0 = row0;
  200.         long *srcp1 = row1;
  201.         long src0, src1;
  202.         short bits = 0;
  203.         char data = 0;
  204.         PGOutputContour* *curVert = &vert[0];
  205.         
  206.         for( short x=b.left; x<b.right; x++ ) {                // Scanline:
  207.             // Load in new longwords, or shift the ones we've got:
  208.             if( --bits <=0 ) {
  209.                 src0 = *(srcp0++);
  210.                 src1 = *(srcp1++);
  211.                 bits = 32;
  212.             } else {
  213.                 src0 <<= 1;
  214.                 src1 <<= 1;
  215.             }
  216.             
  217.             // Shift MSB of each longword into the data, keeping last 4 bits:
  218.             data = (data<<2) & 0x0F;
  219.             if( src0<0 )                    // Set data bit if MSB of src0 set
  220.                 data |= 2;
  221.             if( src1<0 )
  222.                 data |= 1;                    // Set data bit if MSB of src1 set
  223.             
  224.             //$$$$$ I think this bit-shifting will have to be reworked for little-endian CPUs.
  225.  
  226.             // Call appropriate function depending on state of the latest 4 bits.
  227.             // Bits are stored in 'data' as ABCD, corresponding to pixels->  AC
  228.             // where (x,y) is right in between the four pixels.                  BD
  229.             
  230.             switch( data ) {
  231.                 case 8:                        // 1000: Lower-right
  232.                 case 9:                        // 1001: Lower-right / Upper-left
  233.                     CloseContour(*curVert,curHoriz,x,y, vert,b,output);
  234.                     curHoriz = kODNULL;
  235.                     *curVert = kODNULL;
  236.                     
  237.                     if( data==8 )
  238.                         break;
  239.                     // In case 9, FALL THROUGH:
  240.                 case 1:                        // 0001: Upper-left
  241.                 case 14:                    // 1110: Upper-left negative
  242.                     curHoriz = new PGOutputContour(x,y);
  243.                     *curVert = curHoriz;
  244.                     break;
  245.             
  246.                 case 2:                        // 0010: Lower-left
  247.                     curHoriz = *curVert;
  248.                     *curVert = kODNULL;
  249.                     curHoriz->AddVertex(kLeft,x,y);
  250.                     break;
  251.             
  252.                 case 4:                        // 0100: Upper-right
  253.                     curHoriz->AddVertex(kRight,x,y);
  254.                     *curVert = curHoriz;
  255.                     curHoriz = kODNULL;
  256.                     break;
  257.                 
  258.                 case 6: {                    // 0110: Upper-right / Lower-left
  259.                     PGOutputContour *v;
  260.                     v = *curVert;
  261.                     curHoriz->AddVertex(kRight,x,y);
  262.                     *curVert = curHoriz;
  263.  
  264.                     curHoriz = v;
  265.                     curHoriz->AddVertex(kLeft,x,y);
  266.                     break;
  267.                 }
  268.                 case 7:                        // 0111: Lower-right negative
  269.                     CloseContour(curHoriz,*curVert,x,y, vert,b,output);
  270.                     curHoriz = kODNULL;
  271.                     *curVert = kODNULL;
  272.                     break;
  273.                     
  274.                 case 11:                    // 1011: Upper-right negative
  275.                     curHoriz->AddVertex(kLeft,x,y);
  276.                     *curVert = curHoriz;
  277.                     curHoriz = kODNULL;
  278.                     break;
  279.                     
  280.                 case 13:                    // 1101: Lower-left negative
  281.                     curHoriz = *curVert;
  282.                     *curVert = kODNULL;
  283.                     curHoriz->AddVertex(kRight,x,y);
  284.                     break;
  285.                     
  286.                 case 0:                        // Rest of cases are no-ops.
  287.                 case 3:
  288.                 case 5:
  289.                 case 10:
  290.                 case 12:
  291.                 case 15:
  292.                     break;
  293.             }
  294.             
  295.             curVert++;
  296.         }
  297.     }
  298.     
  299.     delete vert;
  300.     DisposePort(&port);
  301.     
  302.     output.BuildPolygon(poly);
  303.     output.DeleteAllLinks();
  304.     
  305. //    SetCursor(*GetCursor(crossCursor));    // $$$$$ TEST
  306. }